#ifndef SIMPLEQUERY_H
#define SIMPLEQUERY_H
#include "configure.h"
#include "siftgeo.h"
#include <string>
#include <iostream>
#include <boost/filesystem.hpp>
#include <ctime>
using namespace std;
namespace bfs = boost::filesystem;
float hamweight[] ={  64.0 , 57.977632187 , 52.9769387503 , 48.5831694924 , 44.6266908811 , 41.0146887491 , 37.6885678971 , 34.6079874262 , 31.7435028457 , 29.0726976596 ,
26.5779529706 , 24.2450700365 , 22.0623728577 , 20.0200977461 , 18.1099629295 , 16.3248555851 , 14.6585979182 , 13.1057677896 , 11.6615576884 , 10.3216609604 ,
9.0821774354 , 7.93953269021 , 6.89040653809 , 5.93166723411 , 5.06030847454 , 4.27338668294 , 3.567956416 , 2.94100210877 , 2.38936496059 , 1.90966474311 ,
1.49821795251 , 1.15095632314 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0  };
class SimpleQuery
{
    private:
    SiftGeo *sg;
    Quantizer *qz;
    vector<pair<float,int> > imagescore;
    vector<string> imagelist;
    int* imagelength;
    int imagenum;
    int bitnum[256];

    Lexicon lex;

    vector<char*>posting;
    vector<size_t>postingsize;

    public:
    SimpleQuery()
    {
        sg = new SiftGeo();
        qz = new Quantizer(CLUST_PATH,HAMATRIX_PATH,HASHCLUST_PATH);
        init_image();
        lex.fromfile(POSTING_PATH+"lexicon");
        init_posting();
        init_bitnum();
    }
    ~SimpleQuery()
    {
        sg->free();
        qz->free();
        delete sg;
        delete qz;
        delete[] imagelength;
        lex.free();
        for(int i=0; i<posting.size(); ++i)
        munmap(posting[i],postingsize[i]);
    }
    void init_image()
    {
        struct stat statbuf;
        stat("imagelength.mat",&statbuf);
        int fid = open("imagelength.mat",O_RDONLY);
        char* buf = reinterpret_cast<char*>(mmap(NULL,statbuf.st_size,PROT_READ,MAP_PRIVATE,fid,0));
        imagenum = *(reinterpret_cast<int*>(buf));
        imagelength = new int[imagenum];
        imagescore = vector<pair<float,int> >(imagenum);
        int* intptr = reinterpret_cast<int*>(buf+sizeof(int));
        for(int i=0; i<imagenum; ++i,++intptr)
            imagelength[i] = *intptr;
        munmap(buf,statbuf.st_size);
        imagelist = vector<string>(imagenum);
        FILE* imfid = fopen("imagelist.txt","r");
        char name[1000];
        for(int i=0; i<imagenum; ++i)
        {
            fgets(name,1000,imfid);
            imagelist[i]= string(name).substr(0,7)+"jpg";
        }
        fclose(imfid);
    }
    void init_posting()
    {
        bfs::path postingdir(POSTING_PATH);
        vector<string>postingname;
        for(bfs::directory_iterator dit(postingdir); dit!=bfs::directory_iterator(); ++dit)
        {
            string name = dit->filename();
            if(name == "lexicon")continue;
            postingname.push_back(dit->string());
        }
        sort(postingname.begin(),postingname.end());
        for(int i=0; i<postingname.size(); ++i)
        {
            struct stat statbuf;
            stat(postingname[i].c_str(),&statbuf);
            int fid = open(postingname[i].c_str(),O_RDONLY);
            char* buf = reinterpret_cast<char*>(mmap(NULL,statbuf.st_size,PROT_READ,MAP_PRIVATE,fid,0));
            posting.push_back(buf);
            postingsize.push_back(statbuf.st_size);
        }
    }
    void init_bitnum()
    {
        for(int i=0; i<256; ++i)
        {
            int j = i;
            bitnum[i] = 0;
            while(j > 0)
            {
                ++bitnum[i];
                j &= (j-1);
            }
        }
    }
    vector<int> query(string siftgeoname,int topn)
    {
        unsigned int start = clock();
        for(int i=0; i<imagenum; ++i)
        {
            imagescore[i].first = 0.0;
            imagescore[i].second = i;
        }
        sg->fromfile(siftgeoname);
        qz->findnn(sg->sift,sg->count);
        qz->gen_hashsift(sg->sift,sg->count);
        qz->gen_hamcode(qz->hashsift,sg->count);

        int tid;
        char* charptr;
        int* intptr;
        int imageid,termfq;
        char dis;
        char* hamcode;
        float weight;
        cout << sg->count << endl;
        for(int i=0; i<sg->count; ++i)
        {
            tid = qz->nn[i];
            charptr = posting[lex.get_postingfid(tid)] + lex.get_startbyte(tid);
            intptr = reinterpret_cast<int*>(charptr);
            weight = log(static_cast<float>(imagenum)/lex.get_docfq(tid));
            for(int j=0; j<lex.get_docfq(tid); ++j)
            {

                imageid = *(intptr++);
                termfq = *(intptr++);
                charptr = reinterpret_cast<char*>(intptr);
                for(int k=0; k<termfq; ++k)
                {
                    dis = 0;
                    hamcode = qz->hamcode + (HAMMING_DIM>>3)*i;
                    for(int s=0; s<(HAMMING_DIM>>3); ++s)
                    {
                        int a = static_cast<unsigned char>((*charptr++)^(*hamcode++));
                        dis += bitnum[a];
                    }
                   if(dis <= MAX_HAMMING_DISTANCE)imagescore[imageid].first += weight*hamweight[dis];
                }
                intptr = reinterpret_cast<int*>(charptr);
            }
        }
        for(int i=0; i<imagenum; ++i)
        {
            if(imagelength[i] == 0) imagescore[i].first = 0;
            else imagescore[i].first /=  sqrt(imagelength[i]);
        }
        partial_sort(imagescore.begin(),imagescore.begin()+topn,imagescore.end(),greater<pair<float,int> >());
        cout << "search time is " << clock()-start<< endl;
        vector<int>champion(topn);
        for(int i=0; i<topn; ++i)
        {
            cout << "image " << imagescore[i].second << ", score = " << imagescore[i].first << ", length = " << imagelength[imagescore[i].second] << endl;
            champion[i] = imagescore[i].second;
        }
        display_result(champion);
        return champion;
    }
    void display_result(vector<int> & rank)
    {
        FILE* front = fopen((HTML_PATH+"front.html").c_str(),"r");
        FILE* back = fopen((HTML_PATH+"back.html").c_str(),"r");
        FILE* result = fopen((HTML_PATH+"result.html").c_str(),"w");
        char line[10000];
        while(fgets(line,10000,front)!=NULL)
            fputs(line,result);
        string sline;
        for(int i=0; i<rank.size(); ++i)
        {

            sline = "<LI><A href=\"file://" + IMAGE_PATH + imagelist[rank[i]] + "\"><IMG src=\"file://" + IMAGE_PATH + imagelist[rank[i]]
                    +"\" alt=\"description\"><IMG src=\"file://" +  IMAGE_PATH + imagelist[rank[i]]+ "\" alt=\"description\" class=\"preview\"></A></LI>";
            fputs(sline.c_str(),result);
        }
        while(fgets(line,10000,back)!=NULL)
            fputs(line,result);
        fclose(result);
        string order = "google-chrome " + HTML_PATH + "result.html";
        system(order.c_str());
    }
};
#endif
